require 'sxrb/callbacks'
require 'sxrb/proxy'

module SXRB
  # Main class of gem, which allows parsing XML data.
  class Parser
    # Create parser object and parse provided input.
    # @param [Filename, IO, String] input
    #   Data stream to be parsed.
    # @option opts [Symbol] :mode (:string)
    #   Instructs SXRB how to treat input parameter. Possible values are:
    #   :string, :file, :io. Please note, that in case of :file mode
    #   input should be filename.
    # @deprecated
    #   This method should not be used anymore. In version 1.0 it will work
    #   completely different so it is preferable to migrate to use parse_*
    #   methods right now.
    def initialize(input, opts = {}, &block)
      raise ArgumentError.new("Block expected") if block.nil?
      case opts[:mode]
      when :file
        self.class.parse_file(input, &block)
      when :io
        self.class.parse_io(input, &block)
      else
        self.class.parse_string(input, &block)
      end
    end

    # Define set of reusable rules for SXRB parser.
    # @yield Proxy
    #   See Proxy documentation for details on available methods and defining
    #   rules.
    # @return [Callbacks]
    #   Set of rules which can be feeded to parse_* methods.
    def self.define_rules(&block)
      Callbacks.new.tap do |cb|
        yield Proxy.new(cb)
      end
    end

    # Parse string containing XML.
    # @param [String] string
    #   Input string
    # @param [optional, Callbacks] callbacks
    #   Set of rules generated by define_rules.
    # @yield [Proxy]
    #   If callbacks parameter is not provided rules have to be defined directly in provided block.
    # @return [nil]
    def self.parse_string(string, callbacks = nil, &block)
      raise ArgumentError unless !!callbacks ^ !!block
      LibXML::XML::SaxParser.string(string).tap do |parser|
	parser.callbacks = callbacks || define_rules(&block)
	parser.parse
      end
      nil
    end

    # Parse file containing XML.
    # @param [String] filename
    #   Path to file to be parsed.
    # @param [optional, Callbacks] callbacks
    #   Set of rules generated by define_rules.
    # @yield [Proxy]
    #   If callbacks parameter is not provided rules have to be defined directly in provided block.
    # @return [nil]
    def self.parse_file(filename, callbacks = nil, &block)
      raise ArgumentError unless !!callbacks ^ !!block
      LibXML::XML::SaxParser.file(filename).tap do |parser|
	parser.callbacks = callbacks || define_rules(&block)
	parser.parse
      end
    end

    # Parse IO containing XML.
    # @param [IO] io
    #   Path to stream to be parsed.
    # @param [optional, Callbacks] callbacks
    #   Set of rules generated by define_rules.
    # @yield [Proxy]
    #   If callbacks parameter is not provided rules have to be defined directly in provided block.
    # @return [nil]
    def self.parse_io(io, callbacks = nil, &block)
      raise ArgumentError unless !!callbacks ^ !!block
      LibXML::XML::SaxParser.io(io).tap do |parser|
	parser.callbacks = callbacks || define_rules(&block)
	parser.parse
      end
    end
  end
end
